package red.stu.mc.mod.codeblock.util.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Set;

public class ServerSocket {

	private final static long SLEEP_TIME_DEFAULT = 2000;
	private final long sleepTime;

	private volatile boolean starting = false;
	private volatile HashMap<Long, ClientSocket> clientSockets = new HashMap<>();
	private long nowCid = 0;

	private java.net.ServerSocket serverSocket;
	private ServerSocketCallback serverSocketCallback;

	public ServerSocket(int port, ServerSocketCallback serverSocketCallback) throws IOException {
		this(port, serverSocketCallback, SLEEP_TIME_DEFAULT);
	}

	public ServerSocket(int port, ServerSocketCallback serverSocketCallback, long sleepTimeDefault) throws IOException {
		this.serverSocketCallback = serverSocketCallback;
		this.sleepTime = sleepTimeDefault;
		serverSocket = new java.net.ServerSocket(port);
		starting = true;
		// 获取连接客户端线程
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (starting) {
					try {
						Socket socket = serverSocket.accept();
						if (serverSocketCallback.onBeforeClientJoin(socket)) {
							long cid = getCID();
							clientSocketsPut(cid, new ClientSocket(socket, cid));
						}
					} catch (IOException e) {
					}
				}
			}
		}).start();

	}

	public void clientSocketsPut(long cid, ClientSocket clientSocket) {
		synchronized (clientSockets) {
			clientSockets.put(cid, clientSocket);
		}
		serverSocketCallback.onClientJoin(clientSocket);
	}

	public void clientSocketsRemove(long cid) {
		ClientSocket clientSocket = null;
		synchronized (clientSockets) {
			clientSocket = clientSockets.remove(cid);
		}
		try {
			serverSocketCallback.onClientExit(clientSocket);
			clientSocket.close();
		} catch (Exception e) {}
	}

	public void clientSocketsClean() {
		synchronized (clientSockets) {
			ClientSocket clientSocket;
			Set<Long> sets = clientSockets.keySet();
			for (Long cid : sets) {
				clientSocket = clientSockets.remove(cid);
				if (clientSocket != null) {
					try {
						serverSocketCallback.onClientExit(clientSocket);
						clientSocket.close();
					} catch (Exception e) {}
				}
			}
		}
	}

	private synchronized long getCID() {
		if (nowCid == Long.MAX_VALUE)
			nowCid = 0;
		return ++nowCid;
	}

	public java.net.ServerSocket getServerSocket() {
		return serverSocket;
	}

	public void setServerSocket(java.net.ServerSocket serverSocket) {
		this.serverSocket = serverSocket;
	}

	public ServerSocketCallback getServerSocketCallback() {
		return serverSocketCallback;
	}

	public void setServerSocketCallback(ServerSocketCallback serverSocketCallback) {
		this.serverSocketCallback = serverSocketCallback;
	}

	public long getSleepTime() {
		return sleepTime;
	}

	public void close() {
		starting = false;
		clientSocketsClean();
		try {
			serverSocket.close();
		} catch (IOException e) {
			System.err.println(e.getMessage());
		}
	}

	public class ClientSocket extends Thread {

		private volatile boolean receiveData = false;

		private long cid;
		private Socket socket = null;
		private BufferedReader socketBr = null;
		private OutputStreamWriter osw = null;

		public ClientSocket(Socket socket, long cid) {
			this.cid = cid;
			try {
				socket.setKeepAlive(true);
			} catch (SocketException e1) {
			}
			this.socket = socket;
			try {
				socketBr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				osw = new OutputStreamWriter(socket.getOutputStream());
				start();
			} catch (Exception e) {
			}
		}

		@Override
		public void run() {
			char[] c = new char[1024];
			String lineStr = null;
			try {
				while ((lineStr = socketBr.readLine()) != null)
					serverSocketCallback.onClientData(this, lineStr);
			} catch (IOException e) {
			}
			this.close();
		}

		public Socket getSocket() {
			return socket;
		}
		
		public long getCid() {
			return cid;
		}

		/**
		 * 当前连接是否中断
		 * 
		 * @return
		 */
		public boolean isConnectClose() {
			try {
				socket.sendUrgentData(0xFF);
				return false;
			} catch (IOException e) {
				return true;
			}
		}

		public void write(String data) throws IOException {
			osw.write(data);
		}

		public void flush() throws IOException {
			osw.flush();
		}

		public void writeEnd(String data) throws IOException {
			this.write(data);
			this.flush();
		}

		public void close() {
			receiveData = false;
			try {
				socket.close();
			} catch (IOException e) {
				System.err.println(e.getMessage());
			}
			clientSocketsRemove(this.cid);
		}

	}

}
